package chapter3.recipe1;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import netty.cookbook.common.http.BasicHttpResponseHandler;
import netty.cookbook.common.http.HttpEventHandler;
@Sharable
public class HttpEventRoutingHandler extends SimpleChannelInboundHandler<Object> {
private Map<UriMatcher, HttpEventHandler> routes = new LinkedHashMap<>();
private Map<String, List<HttpEventHandler>> cachedRoutes = new HashMap<>();
private static final String STARTS_WITH = "startsWith:";
private static final String ENDS_WITH = "endsWith:";
private static final String EQUALS = "equals:";
private static final int MAX_SIZE_POOL_HANDLER = 1000;
private static final BasicHttpResponseHandler HANDLER_404 = new BasicHttpResponseHandler("Not found", 404);
public HttpEventRoutingHandler(Map<String, HttpEventHandler> routes)
throws Exception {
setupRoutes(routes);
}
private class StartsWithMatcher implements UriMatcher {
private String route;
private StartsWithMatcher(String route) {
this.route = route;
}
public boolean match(String uri) {
return uri.startsWith(route);
}
}
private class EndsWithMatcher implements UriMatcher {
private String route;
private EndsWithMatcher(String route) {
this.route = route;
}
public boolean match(String uri) {
return uri.endsWith(route);
}
}
private class EqualsMatcher implements UriMatcher {
private String route;
private EqualsMatcher(String route) {
this.route = route;
}
public boolean match(String uri) {
return uri.equals(route);
}
}
private void setupRoutes(Map<String, HttpEventHandler> routes)
throws Exception {
for (Map.Entry<String, HttpEventHandler> m : routes.entrySet()) {
System.out.println("key " +m.getKey());
if (m.getKey().startsWith(STARTS_WITH)) {
this.routes.put(new StartsWithMatcher(m.getKey().replace(STARTS_WITH,"")), m.getValue());
} else if (m.getKey().startsWith(ENDS_WITH)) {
this.routes.put(new EndsWithMatcher(m.getKey().replace(ENDS_WITH, "")), m.getValue());
} else if (m.getKey().startsWith(EQUALS)) {
this.routes.put(new EqualsMatcher(m.getKey().replace(EQUALS, "")), m.getValue());
} else {
throw new Exception("No matcher found in route " + m.getKey());
}
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg)
throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
String uri = request.getUri();
QueryStringDecoder decoder = new QueryStringDecoder(uri);
String path = decoder.path();
//System.out.println("path "+path);
HttpResponse response = findHandler(request, path).handle(request, decoder);
ctx.write(response);
ctx.flush().close();
}
}
public static int randInt(int min, int max) {
return new Random().nextInt((max - min)) + min;
}
static List<HttpEventHandler> newHttpHandlerPool(int max, HttpEventHandler h){
List<HttpEventHandler> pool = new ArrayList<HttpEventHandler>(max);
for (int i = 0; i < max; i++) {
HttpEventHandler newHandler = HttpEventHandler.deepClone(h);
pool.add(newHandler);
//System.out.println(newHandler);
}
return pool;
}
private HttpEventHandler findHandler(HttpRequest request, String path) throws Exception {
int index = randInt(0, MAX_SIZE_POOL_HANDLER);
List<HttpEventHandler> pool = cachedRoutes.get(path);
if(pool != null){
if(pool.size()>0){
return pool.get(index);
}
}
HttpEventHandler h = null;
for (Map.Entry<UriMatcher, HttpEventHandler> m : routes.entrySet()) {
if (m.getKey().match(path)) {
h = m.getValue();
cachedRoutes.put(path, newHttpHandlerPool(MAX_SIZE_POOL_HANDLER, h));
break;
}
}
if(h != null){
return h;
}
return HANDLER_404;
}
}